#include <NCore.h>

// system headers
#ifndef N_WINDOWS
#  define _GNU_SOURCE
#else
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif // !defined(_CRT_SECURE_NO_WARNINGS)
#ifndef _CRT_NON_CONFORMING_SWPRINTFS
#define _CRT_NON_CONFORMING_SWPRINTFS
#endif // !defined(_CRT_NON_CONFORMING_SWPRINTFS)
#endif

#include <stdio.h>
#include <stdlib.h>
#ifdef N_WINDOWS
	#include <tchar.h>
	#include <Windows.h>
#elif defined(N_LINUX)
	#include <ctype.h>
	#include <unistd.h>
	#include <libgen.h>
	#include <string.h>
	#include <sys/param.h>
	#include <dlfcn.h>
#endif

#include <TutorialUtils.h>
#include "Config.h"

void FreeConfigurations(CfgEntry * pConfig)
{
	CfgEntry * currentElement;

	for (currentElement = pConfig; currentElement->Name != NULL; currentElement++)
	{
		NFree(currentElement->Name);
		NFree(currentElement->Value);
	}

	free(pConfig);
}
static NChar* stripWhitespaces(const NChar * str)
{
	const NChar *end;
	NSizeType out_size;
	NChar * out_str = NULL;
	NSizeType len = strlen(str);

	// Trim leading space
	while(isspace(*str)) str++;
	// Trim trailing space
	end = str + strlen(str) - 1;
	while(end > str && (isspace(*end) || *end == N_T('\r') || *end == N_T('\n'))) end--;
	end++;

	out_size = (NSizeType)(end - str) < len ? (end - str) : len;

	if (NFailed(NCAlloc(sizeof(NChar) * (out_size + 100), (void **)&out_str)))
		return NULL;

	strcpy(out_str, str);
	out_str[out_size] = N_T('\0');

	return out_str;
}

static void CfgParseLine(const NChar* line, CfgEntry * pEntry)
{
	int len = 0;
	NChar name[512] = {N_T('\0'), 0};
	NChar value[512] = {N_T('\0'), 0};
	NChar * name_stripped = NULL;
	NChar * value_stripped = NULL;

	strcpy(name, line);

	while (*line)
	{

		if ((N_T('=') == *line))
		{
			name[len] = 0;
			name_stripped = stripWhitespaces(name);

			if (*line)
			{
				strcpy(value, line + 1);
				value_stripped = stripWhitespaces(value);
			}

			pEntry->Name = name_stripped;
			pEntry->Value = value_stripped;

			return;
		}
		else
		{
			line++;
			len++;
		}
	}
}

CfgEntry * LoadConfigurations(NChar * szPath)
{
	CfgEntry * result, *current = NULL;
	FILE *file = NULL;
	NChar line[1024 + 2];
	int lines = 0;

	if (szPath == NULL)
		return NULL;
	file = fopen(szPath, N_T("r"));
	if (file == NULL)
		return NULL;

	// count lines
	while (fgets(line, (sizeof(line) / sizeof(line[0]) - 1), file) != NULL)
	{
		lines++;
	}
	lines ++;

	result = (CfgEntry *)calloc(lines, sizeof(CfgEntry));
	current = result;

	fseek(file, 0, SEEK_SET);

	while (fgets(line, sizeof(line) / sizeof(line[0]), file) != NULL)
	{
		CfgParseLine(line, current);
		if (current->Name)
		{
			current ++;
		}
	}

	fclose(file);

	return result;
}

NChar * CfgGetValue(CfgEntry * arEntries, const NChar * szValueName)
{
	CfgEntry * currentElement;

	for (currentElement = arEntries; currentElement->Name != NULL; currentElement++)
	{
		if (strcmp(currentElement->Name, szValueName) == 0)
		{
			return currentElement->Value;
		}
	}

	return NULL;
}
